<script lang="ts" setup>
import { nextTick, ref, watch } from 'vue';
import cloneDeep from 'lodash/cloneDeep';
import merge from 'lodash/merge';
import Editor from 'wangeditor';
import type { UploadFile, UploadFiles, FormInstance, UploadInstance } from 'element-plus/es/components';
import type { FormOptions } from '../types/types';
import { Model, thorwPropRepeat } from '../utils';

const props = withDefaults(
  defineProps<{
    options: FormOptions[];
    value: Model;
  }>(),
  {},
);

const defaultModel = ref<Model>(props.value);
const rules = ref<any>();
const form = ref<FormInstance>();

const initForm = () => {
  const r: any = {};
  props.options.forEach((item: FormOptions) => {
    r[item.prop] = item.rules;
    if (item.type === 'editor') {
      initEditor(item);
    }
  });
  rules.value = cloneDeep(r);
};

watch(
  () => props.options,
  () => {
    thorwPropRepeat(props.options) && initForm();
  },
  {
    deep: true,
    immediate: true,
  },
);

const emits = defineEmits(['update:value']);

watch(
  () => props.value,
  (val) => {
    defaultModel.value = val;
  },
  {
    deep: true,
  },
);

watch(
  () => defaultModel.value,
  (val) => {
    emits('update:value', val);
  },
  {
    deep: true,
  },
);

// 上传表单项
const upload = ref<UploadInstance>();
const halderSuccess = (item: FormOptions, response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  defaultModel.value[item.prop] = { response, uploadFile, uploadFiles };
  item.uploadHooks?.onSuccess && item.uploadHooks?.onSuccess(response, uploadFile, uploadFiles);
};

// 编辑表单项
const editMap = new Map<string, Editor>();
function initEditor(item: FormOptions) {
  nextTick(() => {
    if (document.querySelector(`#${item.prop}`)) {
      const editor = new Editor(`#${item.prop}`);
      editor.config.placeholder = item.placeholder ?? '';
      editor.create();
      editor.txt.html(item.value);
      editor.config.onchange = (newHtml: string) => {
        defaultModel.value[item.prop] = newHtml;
      };
      editMap.set(item.prop, editor);
    }
  });
}
const resetFields = () => {
  form.value?.resetFields();
  if (props.options && props.options.length) {
    props.options.forEach((item: FormOptions) => {
      if (item.type === 'editor') {
        editMap.get(item.prop)?.txt.html(item.value);
      }
    });
  }
};
defineExpose({
  resetFields,
});
</script>

<template>
  <el-form ref="form" :model="defaultModel" :rules="rules" v-bind="$attrs">
    <template v-for="(item, index) in options" :key="index">
      <el-form-item v-if="item.children && item.children.length" :prop="item.prop" :label="item.label">
        <component
          v-bind="item.attrs"
          :is="`el-${item.type}`"
          v-model="defaultModel[item.prop!]"
          :placeholder="item.placeholder"
        >
          <component
            :is="`el-${child.type}`"
            v-for="(child, i) in item.children"
            :key="i"
            :label="child.label"
            :value="child.value"
          ></component>
        </component>
      </el-form-item>
      <el-form-item v-else :label="item.label" :prop="item.prop">
        <el-upload
          v-if="item.type === 'upload'"
          ref="upload"
          :on-success="(...args) => halderSuccess(item, ...args)"
          v-bind="merge(item.uploadAttrs, item.uploadHooks)"
        >
          <template #tip>
            <slot name="uploadTip"></slot>
          </template>
          <template #trigger>
            <slot name="uploadArea"></slot>
          </template>
        </el-upload>
        <div v-else-if="item.type === 'editor'" :id="item.prop"></div>
        <component
          v-bind="item.attrs"
          :is="`el-${item.type}`"
          v-else
          v-model="defaultModel[item.prop!]"
          :placeholder="item.placeholder"
        ></component>
      </el-form-item>
    </template>
    <el-form-item>
      <slot name="action" :form="form"></slot>
    </el-form-item>
  </el-form>
</template>

<style lang="scss" scoped></style>
